use ggez::graphics::*;
use ggez::{Context, GameResult};
use glam::*;

use crate::drawing::UnitDrawable;
use crate::drawing::*;
use crate::logic;
use crate::view::{self, FrameNumber, TILE_SIZE};

impl crate::view::Cursor {
    pub fn current_offset(&self, frame_number: FrameNumber) -> Vec2 {
        self.current_tile_offset(frame_number) * TILE_SIZE
    }

    pub fn as_drawable(&self, frame_number: FrameNumber, ctx: &mut Context) -> GameResult<Mesh> {
        let color = Color {
            a: ((frame_number as f32 / 60.0).sin() + 1.0) / 2.0 / 2.0 + 0.1,
            ..Color::YELLOW
        };

        Mesh::new_rectangle(
            ctx,
            DrawMode::Fill(FillOptions::default()),
            Rect::new(0.0, 0.0, TILE_SIZE, TILE_SIZE),
            color,
        )
    }
}

pub fn draw_floor(
    ctx: &DrawContext,
    logic_floor: &logic::Floor,
    view_floor: &view::Floor,
) -> GameResult {
    let tile_count_in_dim_x = 9.0;
    let actual_tile_size = ctx.dim.x / tile_count_in_dim_x;
    let tile_count_in_dim_y = ctx.dim.y / actual_tile_size;
    let scale = actual_tile_size / TILE_SIZE;
    let scale_mat = Mat4::from_scale(vec3(scale, scale, 1.0));

    let cursor_offset = view_floor.cursor.current_offset(ctx.frame_number);
    let center_offect_x = TILE_SIZE * (tile_count_in_dim_x - 1.0) / 2.0;
    let center_offect_y = TILE_SIZE * (tile_count_in_dim_y - 1.0) / 2.0;
    let base_translation =
        Mat4::from_translation(vec3(cursor_offset.x * -1.0, cursor_offset.y * -1.0, 0.0))
            * Mat4::from_translation(vec3(center_offect_x, center_offect_y, 0.0));
    let base_transform = ctx.transform * scale_mat * base_translation;

    let mut tile_borders_builder = MeshBuilder::new();
    let floor_width = logic_floor.size.width;
    let floor_height = logic_floor.size.height;

    let right = floor_width as f32 * TILE_SIZE;
    let bottom = floor_height as f32 * TILE_SIZE;

    let border_color = Color {
        a: 0.2,
        ..Color::BLACK
    };

    for col in 0..floor_width + 1 {
        let x = col as f32 * TILE_SIZE;
        tile_borders_builder.line(
            &[Vec2::new(x, 0.0), Vec2::new(x, bottom)],
            4.0,
            border_color,
        )?;
    }

    for row in 0..floor_height + 1 {
        let y = row as f32 * TILE_SIZE;
        tile_borders_builder.line(&[Vec2::new(0.0, y), Vec2::new(right, y)], 4.0, border_color)?;
    }

    let tile_borders = tile_borders_builder.build(&mut ctx.ggez_ctx.borrow_mut())?;

    draw(
        &mut ctx.ggez_ctx.borrow_mut(),
        &tile_borders,
        DrawParam::default().transform(base_transform),
    )?;

    for pos in logic_floor.size.iter_positions() {
        let x = TILE_SIZE * pos.x() as f32;
        let y = TILE_SIZE * pos.y() as f32;
        let local_translation = Mat4::from_translation(vec3(x, y, 0.0));
        let local_transform = base_transform * local_translation;
        let local_params = DrawParam::default().transform(local_transform);

        if let Some(terrian) = logic_floor.terrian_grid.at(&pos) {
            let terrian_drawable = &terrian.as_drawable(&mut ctx.ggez_ctx.borrow_mut())?;

            draw(
                &mut ctx.ggez_ctx.borrow_mut(),
                terrian_drawable,
                local_params,
            )?;
        }

        if let Some(unit) = logic_floor.unit_grid.at(&pos).and_then(|u| u.upgrade()) {
            let unit_drawable =
                unit.as_drawable(ctx.frame_number, &mut ctx.ggez_ctx.borrow_mut())?;
            draw(&mut ctx.ggez_ctx.borrow_mut(), &unit_drawable, local_params)?;
        }
    }

    let cursor_drawable = view_floor
        .cursor
        .as_drawable(ctx.frame_number, &mut ctx.ggez_ctx.borrow_mut())?;

    draw(
        &mut ctx.ggez_ctx.borrow_mut(),
        &cursor_drawable,
        DrawParam::default().transform(
            base_transform * Mat4::from_translation(vec3(cursor_offset.x, cursor_offset.y, 0.0)),
        ),
    )?;

    Ok(())
}
